(function ($) {
	var olscheckboxtree = {
		options:{
			ontology_id: '',
			ids: [],
			service_url :'http://www.ebi.ac.uk/ontology-lookup/json/',
			check_children: true,
	        check_parents: true,
	        collapsable: true,
	        collapsed: false,
	        selected: false,
			collapse_duration: 800,
	        collapse_effect: 'blind',
	        collapse_image: '',
	        container: 'olscheckboxTree',
	        css_class: 'olscheckboxTree',
	        expand_duration: 800,
	        expand_effect: 'blind',
	        expand_image: '',
	        leaf_image: '',
			bg_color: '',
			on_state_changed:null,
			selected_ids:[],
			unselected_ids:[]
		},
		_collapse_anchor : '',
	    _expand_anchor : '',
	    _leaf_anchor : '',
		_init: function(){
			var self = this;
			
			if(self.options.bg_color.length > 0){
				 $(self.element).css('background-color', self.options.bg_color);
			}
			
			self._collapse_anchor = (self.options.collapse_image.length > 0) ? '<img src="'+self.options.collapse_image+'" />' : '-';
		    self._expand_anchor = (self.options.expand_image.length > 0) ? '<img src="'+self.options.expand_image+'" />' : '+';
		    self._leaf_anchor = (self.options.leaf_image.length > 0) ? '<img src="'+self.options.leaf_image+'" />' : '';
			
			var url = this.options.service_url+escape('trimmedtree?ontology='+this.options.ontology_id+'&termId='+this.options.ids.join('&termId='));
			$.ajax({
				type: "GET",
				url: url,
				dataType: "json",
				success: function(json){self._parse_response(json)},
				error: function(){alert('Could not contact the OLS service')}
			});
		},
		destroy: function(){
			this.element.empty();
			$.Widget.prototype.destroy.call(this);
		},
		_get_nodes:function(checked){
			var nodes = [];
			var self = this;
			
			$(self.element).find(':input').each(function(){
				if ($(this).is(":checked") == checked){
					nodes.push({id:$(this).attr('name'), name:$(this).attr('value')});
				}
			});
			return nodes;
		},
		_check:function(termId, check){
			termId = termId.replace(':','\\:');
			var self = this;
			$('input[name='+termId+']').each(function(){
				var chk = $(this);
				if(chk.attr('checked') != check){
					chk.click();
					self._on_checkbox_click(chk, self);
				}
			});
		},
		_on_checkbox_click : function(chk, self){
			if (self.options.check_children) {
	            self._toggleChildren(chk);
	        }
	
	        if (self.options.check_parents && chk.is(":checked")) {
	        	self._checkParents(chk);
	        }
		},
		get_checked : function(){
			return this._get_nodes(true);
		},
		get_unchecked : function(){
			return this._get_nodes(false);
		},
		_parse_response: function(json){
			var list = [];
			var nod = {};
			var selected = (this.options.selected == true) ? 'checked="yes"' : '';
			for (var i = 0, l = json.length; i < l; i++) {
				var node = json[i];
	 			this._parse_node(node, list, selected, nod);
			}
			this._setup_tree('<ul class="'+this.options.container+' '+this.options.cssClass+'">'+list.join('')+'</ul>');
		},
		_unique: function(arr){
			var aux = {};
			for(var i in arr){
				aux[arr[i].name] = arr[i];
			}
			var new_arr = [];
			for(var k in aux){
				new_arr.push(aux[k]);
			}
			return new_arr;
		},
		_parse_node: function(node, list, selected, nodes){
			if (typeof nodes[node.name] == 'undefined') {
				nodes[node.name] = node;
				list.push('<li><label><input type="checkbox" ' + selected + ' name="' + node.id + '" value="' + node.name + '" />' + node.name + '</label>');
				if (node.children) {
					var chds = this._unique(node.children);
					list.push('<ul>');
					for (var i = 0, l = chds.length; i < l; i++) {
						var chd = chds[i];
						this._parse_node(chd, list, selected, nodes);
					}
					list.push('</ul>');
				}
				list.push('</li>');
			}
		},
		_setup_tree: function(tree_str){
			 var self = this;
			 var list = self.element.html(tree_str);
			 $("li", list).each(function() {
	
	            if (self.options.collapsable) {
					var $a;
	                if ($(this).is(":has(ul)")) {
	                    if (self.options.collapsed) {
	                        $(this).find("ul").hide();
	                        $a = $('<span></span>').html(self._expand_anchor).addClass("collapsed");
	                    } else {
	                        $a = $('<span></span>').html(self._collapse_anchor).addClass("expanded");
	                    }
	                } else {
	                     $a = $('<span></span>').html(self._leaf_anchor).addClass("leaf");
	                }
	                $(this).prepend($a);
	            }
	        });
			// handle single expand/collapse
			var on_span_click = function(e, a){
				if ($(this).hasClass("leaf") == undefined) {
	                return;
	            }
	
	            if ($(this).hasClass("collapsed")) {
	                self._expand($(this));
	            } else {
	                self._collapse($(this));
	            }
			}
	        this.element.find('span').bind("click", on_span_click);
	
			/*
			 * Select and unselect terms
			 */
	        for(id in self.options.selected_ids){
				self._check(self.options.selected_ids[id],true);
			}
			
			for(id in self.options.unselected_ids){
				self._check(self.options.unselected_ids[id],false);
			}
			
			var on_checkbox_click = function(e, a){
				if (self.options.check_children) {
	                self._toggleChildren($(this));
	            }
	
	            if (self.options.check_parents && $(this).is(":checked")) {
	                self._checkParents($(this));
	            }
				
				if(typeof self.options.on_state_changed == 'function'){
					self.options.on_state_changed();
				}
			}
			// handle tree select/unselect
	        this.element.find(':checkbox').bind("click", on_checkbox_click);
			if(this.get_unchecked().length > 0 && typeof self.options.on_state_changed == 'function'){
				self.options.on_state_changed();
			}
		},
		/**
	     * Collapse tree element
	     */
	    _collapse : function(img){
	        var listItem = img.parents("li:first");
	
	        if ($.ui !== undefined) {
	            listItem.children("ul").hide(this.options.collapse_effect, {}, this.options.collapse_duration);
	        } else {
	            listItem.children("ul").hide(this.options.collapse_duration);
	        }
	
	        listItem.children("span").html(this._expand_anchor).addClass("collapsed").removeClass("expanded");
	    },
		/**
	     * Recursively check parents of passed checkbox
	     */
	    _checkParents : function(checkbox)
	    {
	        var parentCheckbox = checkbox.parents("li:first").parents("li:first").find(" :checkbox:first");
	
	        if (!parentCheckbox.is(":checked")) {
	            parentCheckbox.attr("checked","checked");
	        }
	
	        if (parentCheckbox.parents('[class*=' + this.options.container + ']').attr('class') != undefined) {
	            this._checkParents(parentCheckbox);
	        }
	    },
	    /**
	     * Expand tree element
	     */
	    _expand : function(img){
	        var listItem = img.parents("li:first");
	
	        if ($.ui !== undefined) {
	            listItem.children("ul").show(this.options.expand_effect, {}, this.options.expand_duration);
	        } else {
	            listItem.children("ul").show(this.options.expand_duration);
	        }
	
	        listItem.children("span").html(this._collapse_anchor).addClass("expanded").removeClass("collapsed");
	    },
	
	    /**
	     * Check/uncheck children of passed checkbox
	     */
	    _toggleChildren : function(checkbox){
	        checkbox.parents('li:first').find('li :checkbox').attr('checked',checkbox.attr('checked') ? 'checked' : '');
	    }
	};
	$.widget ('ui.olscheckboxtree', olscheckboxtree);
})(jQuery);